package com.gitee.fastmybatis.core.query;

import com.gitee.fastmybatis.core.FastmybatisContext;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.PageResult;
import com.gitee.fastmybatis.core.ext.MapperRunner;
import com.gitee.fastmybatis.core.mapper.BaseMapper;
import com.gitee.fastmybatis.core.mapper.OneResult;
import com.gitee.fastmybatis.core.query.param.PageParam;
import com.gitee.fastmybatis.core.support.Getter;
import com.gitee.fastmybatis.core.util.ClassUtil;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * LambdaQuery
 * <pre>
 * {@literal
 * LambdaQuery<User> query = new LambdaQuery<>(User.class);
 * query.eq(User::getUserName, "111");
 *
 * LambdaQuery<User> query1 = new LambdaQuery<>(User.class);
 *          .eq(User::getUserName, "222")
 *          .eq(User::getId, 2);
 * }
 * </pre>
 *
 * @param <T>
 * @author tanghc
 */
public class LambdaQuery<T> extends TenantQuery implements LambdaConditionAnd<LambdaQuery<T>, T>, LambdaConditionOr<LambdaQuery<T>, T> {

    private static final long serialVersionUID = 4991634394676333413L;

    private final SetBlock set = new SetBlock();

    private final Class<T> entityClass;

    /**
     * 推荐使用com.gitee.fastmybatis.core.query.LambdaQuery#create(java.lang.Class)
     *
     * @param entityClass 实体类
     * @see #create(Class)
     */
    public LambdaQuery(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public static <T> LambdaQuery<T> create(Class<T> entityClass) {
        return Query.create(entityClass);
    }

    public LambdaQuery<T> andLambda(LambdaConditionChain<T> conditionChain) {
        this.getCondition().subCondition(Joint.AND, conditionChain.apply(new LambdaCondition<>()));
        return this;
    }

    public LambdaQuery<T> orLambda(LambdaConditionChain<T> conditionChain) {
        this.getCondition().subCondition(Joint.OR, conditionChain.apply(new LambdaCondition<>()));
        return this;
    }

    public SetBlock getSet() {
        return set;
    }

    /**
     * 添加条件
     *
     * @param column 字段
     * @param value  条件值，可以是单值/数组/集合
     * @return 返回自身
     */
    public LambdaQuery<T> addCondition(Getter<T, ?> column, Object value) {
        if (value != null) {
            if (value instanceof Collection<?>) {
                this.in(column, (Collection<?>) value);
            } else if (value.getClass().isArray()) {
                Object[] array = (Object[]) value;
                this.in(column, array);
            } else {
                this.eq(column, value);
            }
        }
        return this;
    }

    /**
     * 指定返回列
     *
     * @param column 表字段，可填多个
     * @return 返回Query实例
     */
    @SafeVarargs
    public final LambdaQuery<T> select(Getter<T, ?>... column) {
        List<String> columns = Arrays.stream(column)
                .map(this::getColumnName)
                .collect(Collectors.toList());
        this.getSelectColumns().addAll(columns);
        return this;
    }

    /**
     * 根据字段排序，如果布尔值为true则添加排序，否则不添加
     *
     * @param bool   布尔值
     * @param column 字段
     * @param sort   排序类型
     * @return 返回自身
     */
    public LambdaQuery<T> orderBy(boolean bool, Getter<T, ?> column, Sort sort) {
        if (bool) {
            String columnName = getColumnName(column);
            orderby(columnName, sort);
        }
        return this;
    }

    protected String getColumnName(Getter<T, ?> column) {
        String columnName = ClassUtil.getColumnName(column);
        return FastmybatisContext.getDialect(entityClass).wrap(columnName);
    }

    /**
     * 根据字段排序
     *
     * @param column 字段
     * @param sort   排序类型
     * @return 返回自身
     */
    public LambdaQuery<T> orderBy(Getter<T, ?> column, Sort sort) {
        orderBy(true, column, sort);
        return this;
    }

    /**
     * 根据字段顺序排序
     *
     * @param column 字段
     * @return 返回自身
     */
    public LambdaQuery<T> orderByAsc(Getter<T, ?> column) {
        orderBy(column, Sort.ASC);
        return this;
    }


    /**
     * 根据字段倒序排序
     *
     * @param column 字段
     * @return 返回自身
     */
    public LambdaQuery<T> orderByDesc(Getter<T, ?> column) {
        orderBy(column, Sort.DESC);
        return this;
    }

    /**
     * 设置分页大小
     *
     * @param pageIndex 当前第几页,从1开始
     * @param pageSize  每页结果集大小
     * @return 返回自身
     */
    public LambdaQuery<T> page(int pageIndex, int pageSize) {
        super.page(pageIndex, pageSize);
        return this;
    }

    /**
     * 设置字段值
     *
     * @param getter 数据库字段
     * @param value  值
     * @return 返回自身
     */
    public LambdaQuery<T> set(Getter<T, ?> getter, Object value) {
        set.put(this.getColumnName(getter), value);
        return this;
    }

    /**
     * 自定义set表达式，如：version=version+1
     *
     * @param expression 表达式，如：update_time=now()
     * @return 返回自身
     */
    public LambdaQuery<T> setExpression(String expression) {
        set.addExpression(expression);
        return this;
    }


    @Override
    public LambdaQuery<T> ignoreLogicDeleteColumn() {
        super.ignoreLogicDeleteColumn();
        return this;
    }

    // ACTION

    protected MapperRunner<BaseMapper<T>> getMapperRunner() {
        if (entityClass == null) {
            throw new IllegalArgumentException("未指定entityClass, 使用 Query.query(Class) 方法创建Query");
        }
        return FastmybatisContext.getCrudMapperRunner(entityClass);
    }

    /**
     * 查询一条记录, 追加limit 1
     *
     * @return 返回一条记录，没有返回null
     * @see #getOne() getOne():不会追加limit 1
     */
    public T get() {
        return getMapperRunner().run(mapper -> mapper.get(this));
    }

    /**
     * 查询一条记录,不会追加limit 1
     *
     * @return 返回包装结果
     * @see #getOne(boolean) 指定是否追加limit 1
     */
    public OneResult<T> getOne() {
        return getMapperRunner().run(mapper -> mapper.getOne(this));
    }

    /**
     * 查询一条记录
     *
     * @param appendLimitOne 是否追加limit 1,如果true，始终返回1条数据
     * @return 返回包装结果
     */
    public OneResult<T> getOne(boolean appendLimitOne) {
        return getMapperRunner().run(mapper -> mapper.getOne(this, appendLimitOne));
    }

    /**
     * 返回某一列的值
     *
     * @param column 列
     * @param <V>    值
     * @return 返回值，可能返回null
     */
    public <V> V getValue(Getter<T, V> column) {
        return getMapperRunner().run(mapper -> mapper.getValue(this, column));
    }

    /**
     * 返回某一列的值
     *
     * @param column 列
     * @param <V>    值
     * @return 返回Optional
     */
    public <V> Optional<V> getValueOptional(Getter<T, V> column) {
        return getMapperRunner().run(mapper -> mapper.getValueOptional(this, column));
    }

    /**
     * 查询一条Optional记录,追加limit 1
     *
     * @return 返回一条Optional记录
     */
    public Optional<T> getOptional() {
        return getMapperRunner().run(mapper -> mapper.getOptional(this));
    }

    /**
     * 查询列表
     *
     * @return 返回查询集合，没有返回空list
     */
    public List<T> list() {
        return getMapperRunner().run(mapper -> mapper.list(this));
    }

    /**
     * 查询总数
     *
     * @return 返回总数
     */
    public long getCount() {
        return getMapperRunner().run(mapper -> mapper.getCount(this));
    }

    /**
     * 查询列表并转换
     * <pre>
     * {@literal
     * List<UserVO> list = this.query()
     *  .gt(TUser::getAge, 20)
     *  .list(user -> {
     *      UserVO user = new UserVO();
     *      user.setXx();
     *      return user;
     *  })
     * }
     * </pre>
     *
     * @param converter 转换器
     * @param <R>       返回类型
     * @return 返回转换后的数据
     */
    public <R> List<R> list(Function<T, R> converter) {
        return this.list()
                .stream()
                .map(converter)
                .collect(Collectors.toList());
    }

    /**
     * 查询某一列的值
     * <p>
     * SELECT name FROM table WHERE ...
     * </p>
     *
     * @param column 列
     * @param <R>    列类型
     * @return 返回某一列值List
     */
    public <R> List<R> listValue(Getter<T, R> column) {
        return getMapperRunner().run(mapper -> mapper.listValue(this, column));
    }

    /**
     * 查询某一列的值，去重
     * <p>
     * SELECT name FROM table WHERE ...
     * </p>
     *
     * @param column 列
     * @param <R>    列类型
     * @return 返回某一列值List
     */
    public <R> List<R> listUniqueValue(Getter<T, R> column) {
        return getMapperRunner().run(mapper -> mapper.listUniqueValue(this, column));
    }


    /**
     * 分页查询
     *
     * @param pageIndex 当前第几页,从1开始
     * @param pageSize  每页结果集大小
     * @return 返回自身
     */
    public PageInfo<T> paginate(int pageIndex, int pageSize) {
        super.page(pageIndex, pageSize);
        return getMapperRunner().run(mapper -> mapper.page(this));
    }

    /**
     * 分页查询
     *
     * @param pageParam 分页参数
     * @return 返回自身
     */
    public PageInfo<T> paginate(PageParam pageParam) {
        return this.paginate(pageParam.getPageIndex(), pageParam.getPageSize());
    }

    /**
     * 分页查询，指定返回结果
     *
     * @param pageIndex          当前第几页,从1开始
     * @param pageSize           每页结果集大小
     * @param pageResultSupplier 分页返回结果
     * @return 返回分页信息
     */
    public <P extends PageResult<T>> P paginate(int pageIndex, int pageSize, Supplier<P> pageResultSupplier) {
        this.page(pageIndex, pageSize);
        return getMapperRunner().run(mapper -> mapper.page(this, pageResultSupplier));
    }


    /**
     * 查询并对结果分组
     * <pre>
     * {@literal
     * Map<Long, List<SysDictValue>> map = this.query().group(SysDictValue::getItemId);
     * }
     * </pre>
     *
     * @param keyGetter 分组key
     * @param <K>       分组key
     * @return 返回分组数据
     */
    public <K> Map<K, List<T>> group(Function<T, K> keyGetter) {
        return getMapperRunner().run(mapper -> mapper.getMapGrouping(this, keyGetter));
    }

    /**
     * 查询并对结果分组
     * <pre>
     * {@literal
     * Map<Long, List<SysDictValueBO>> map = this.query()
     *   .group(SysDictValue::getItemId, sysDictValue -> {
     *     return CopyUtil.copyBean(sysDictValue, SysDictValueBO::new);
     *   });
     * }
     * </pre>
     *
     * @param keyGetter   分组key
     * @param valueGetter 值转换
     * @param <K>         分组key
     * @return 返回分组数据
     */
    public <K, V> Map<K, List<V>> group(Function<T, K> keyGetter, Function<T, V> valueGetter) {
        return getMapperRunner().run(mapper -> mapper.getMapGrouping(this, keyGetter, valueGetter));
    }

    /**
     * 查询结果并转换成Map对象<br>
     * 通过list中的某一列（如主键id）当做key返回map对象<br>
     * 如果key重复则抛出异常
     * <pre>
     * {@literal
     * public class User {
     *     private Integer id;
     *     private String name;
     * }
     *
     * List<User> -> Map<Integer, User> // 键:id, 值:当前对象
     * List<User> -> Map<Integer, String> // 键:id, 值:name字段
     * }
     * </pre>
     *
     * @param keyGetter   指定map中的key，确保唯一性，一般使用主键id或唯一索引列
     * @param valueGetter 指定map中的值
     * @param <K>         key类型
     * @param <V>         value类型
     * @return 返回map对象
     */
    public <K, V> Map<K, V> map(Function<T, K> keyGetter, Function<T, V> valueGetter) {
        return map(keyGetter, valueGetter, (u, v) -> {
            throw new IllegalStateException(String.format("Duplicate key %s", u));
        });
    }

    /**
     * 查询结果并转换成Map对象<br>
     * 通过list中的某一列（如主键id）当做key返回map对象
     * <pre>
     * {@literal
     * public class User {
     *     private Integer id;
     *     private String name;
     * }
     *
     * List<User> -> Map<Integer, User> // 键:id, 值:当前对象
     * List<User> -> Map<Integer, String> // 键:id, 值:name字段
     * }
     * </pre>
     *
     * @param keyGetter     指定map中的key，确保唯一性，一般使用主键id或唯一索引列
     * @param valueGetter   指定map中的值
     * @param mergeFunction key冲突返回哪个值
     * @param <K>           key类型
     * @param <V>           value类型
     * @return 返回map对象
     */
    public <K, V> Map<K, V> map(Function<T, K> keyGetter, Function<T, V> valueGetter, BinaryOperator<V> mergeFunction) {
        return getMapperRunner().run(mapper -> mapper.getMap(this, keyGetter, valueGetter, mergeFunction));
    }

    /**
     * list查询并转换结果
     * <p>
     *     <b>注:仅此方法支持group by查询</b>
     * </p>
     * <pre>
     * {@literal
     * List<StateVO> list = userMapper.query()
     *           .select("state, count(*) as state_cnt")
     *           .gt(TUser::getId, 1)
     *           .groupBy(TUser::getState)
     *           .having("state_cnt > 0")
     *           .list(StateVO.class);
     * System.out.println(list);
     * }
     * </pre>
     * @param cls 转换类
     * @param <V> 类型
     * @return 返回转换结果
     */
    public <V> List<V> list(Class<V> cls) {
        return getMapperRunner().run(mapper -> mapper.list(this, cls));
    }

    /**
     * 返回基本类型值
     * <pre>
     * {@literal
     * BigDecimal val = userMapper.query()
     *                 .select("sum(money)")
     *                 .gt(TUser::getId, 1)
     *                 .getPrimitive(BigDecimal.class)
     *                 // 如果返回多行数据这里会抛出异常
     *                 .getOrThrow();
     *  System.out.println(val);
     * }
     * </pre>
     * @param clazz 基本类型值,如:Integer.class, BigDecimal.class, Date.class,类型不对将抛出IllegalArgumentException异常
     * @return 返回OneResult
     * @param <V> 类型
     */
    public  <V> OneResult<V> getPrimitive(Class<V> clazz) {
        return getMapperRunner().run(mapper -> mapper.getPrimitive(this, clazz));
    }

    /**
     * 修改操作<br>
     * <b>不支持</b>全表更新(不加条件),如果是全表更新,会抛UnsupportedOperationException异常.
     *
     * @return 返回影响行数
     */
    public int update() {
        if (this.getCondition().isEmpty()) {
            throw new UnsupportedOperationException("不支持全表更新,请设置修改条件");
        }
        return getMapperRunner().run(mapper -> mapper.update(this));
    }

    /**
     * 条件删除，有逻辑删除字段做更新操作
     *
     * @return 返回影响行数
     */
    public int delete() {
        if (this.getCondition().isEmpty()) {
            throw new IllegalArgumentException("删除条件不能为空");
        }
        this.disableForceQuery();
        return getMapperRunner().run(mapper -> mapper.deleteByQuery(this));
    }

    /**
     * 条件删除，无视逻辑删除字段，执行DELETE操作
     *
     * @return 返回分页信息
     */
    public int deleteForce() {
        if (this.getCondition().isEmpty()) {
            throw new IllegalArgumentException("删除条件不能为空");
        }
        this.enableForceQuery();
        return getMapperRunner().run(mapper -> mapper.deleteByQuery(this));
    }

    @Override
    public LambdaQuery<T> select(String... column) {
        super.select(column);
        return this;
    }

    @Override
    public LambdaQuery<T> clone() {
        return this.copy().toLambdaQuery(this.entityClass);
    }

    /**
     * groupBy
     * <pre>
     * {@literal
     * List<StateVO> list = userMapper.query()
     *                 .select("state, count(*) as cnt")
     *                 .gt(TUser::getId, 1)
     *                 .groupBy(TUser::getState)
     *                 .list(StateVO.class);
     * }
     * </pre>
     * @param columns 字段信息
     * @return 返回自身
     */
    @SafeVarargs
    public final LambdaQuery<T> groupBy(Getter<T, ?>... columns) {
        String groupBy = Arrays.stream(columns)
                .map(this::getColumnName)
                .collect(Collectors.joining(","));
        groupBy(groupBy);
        return this;
    }

    public LambdaQuery<T> having(String having) {
        super.having(having);
        return this;
    }

}
